package collection;

import exception.CollectionEmptyException;

public class ArrayCompleteBinTree<E> extends ACompleteBinTree<E> {

	private FlexibleArray<BinTreeNodeWithIndex<E>> nodeArray = new FlexibleArray<BinTreeNodeWithIndex<E>>(2);

	public ArrayCompleteBinTree()
	{
		this.nodeArray.add(null);
	}
	
	@Override
	public BinTreeNodeWithIndex<E> getParent(BinTreeNodeWithIndex<E> someNode) 
	{
		return this.nodeArray.get(someNode.getIndex()/2);
	}

	@Override
	public boolean hasLeftChild(BinTreeNodeWithIndex<E> someNode) 
	{
		BinTreeNodeWithIndex<E> leftChild;
		try
		{
			leftChild = this.nodeArray.get(someNode.getIndex()*2);
		}
		catch(IndexOutOfBoundsException ex)
		{
			return false;
		}
		
		return leftChild != null;
	}

	@Override
	public boolean hasRightChild(BinTreeNodeWithIndex<E> someNode) 
	{
		BinTreeNodeWithIndex<E> rightChild;
		try
		{
			rightChild = this.nodeArray.get(someNode.getIndex()*2+1);
		}
		catch(IndexOutOfBoundsException ex)
		{
			return false;
		}
		
		return rightChild != null;
	}

	@Override
	public BinTreeNodeWithIndex<E> getLeftChild(BinTreeNodeWithIndex<E> someNode) 
	{
		BinTreeNodeWithIndex<E> leftChildNode;
		try 
		{
			leftChildNode = this.nodeArray.get(someNode.getIndex()*2);
		}
		catch(IndexOutOfBoundsException ex)
		{
			return null;
		}
		
		return leftChildNode;
	}

	@Override
	public BinTreeNodeWithIndex<E> getRightChild(BinTreeNodeWithIndex<E> someNode) 
	{
		BinTreeNodeWithIndex<E> rightChildNode;
		try 
		{
			rightChildNode = this.nodeArray.get(someNode.getIndex()*2+1);
		}
		catch(IndexOutOfBoundsException ex)
		{
			return null;
		}
		
		return rightChildNode;
	}

	@Override
	public void addLeftChildElement(BinTreeNodeWithIndex<E> someNode, E element) 
	{
		int leftChildIndex = someNode.getIndex() * 2;
		this.nodeArray.set(leftChildIndex, new BinTreeNodeWithIndex<E>(element, leftChildIndex));
	}

	@Override
	public void addRightChildElement(BinTreeNodeWithIndex<E> someNode, E element) 
	{
		int rightChildIndex = someNode.getIndex() * 2 + 1;
		this.nodeArray.set(rightChildIndex, new BinTreeNodeWithIndex<E>(element, rightChildIndex));
	}

	@Override
	public BinTreeNodeWithIndex<E> createNode(E element) 
	{
		return new BinTreeNodeWithIndex<E>(element, 0);
	}
	
	@Override
	public BinTreeNodeWithIndex<E> getRoot() throws CollectionEmptyException 
	{
		if (isEmpty())
			throw new CollectionEmptyException("Complete Binary Tree");
		else
			return this.nodeArray.get(1);
	}

	@Override
	public BinTreeNodeWithIndex<E> createRoot(E element) 
	{
		BinTreeNodeWithIndex<E> rootNode = new BinTreeNodeWithIndex<E>(element, 1);
		this.nodeArray.add(1, rootNode);
		return rootNode;
	}

	@Override
	public boolean isRoot(BinTreeNodeWithIndex<E> someNode) 
	{
		return someNode == getRoot();
	}
	
	@Override
	public void addElement(E element) 
	{
		int index = this.nodeArray.size();
		BinTreeNodeWithIndex<E> node = new BinTreeNodeWithIndex<E>(element, index); 
		this.nodeArray.add(index, node);
	}

	@Override
	public BinTreeNodeWithIndex<E> removeLast() 
	{
		return this.nodeArray.remove();
	}

	@Override
	public int size() 
	{
		return this.nodeArray.size - 1;
	}
	
	public BinTreeNodeWithIndex<E> getLastNode()
	{
		if (isEmpty())
			throw new CollectionEmptyException("Complete Binary Tree");
		else
			return this.nodeArray.get(this.nodeArray.size-1);
	}
	
	public E getLastElement()
	{
		return getLastNode().getElement();
	}

	@Override
	public void removeNode(BinTreeNodeWithIndex<E> someNode) 
	{
		this.nodeArray.remove(someNode.getIndex());
	}

}
